package handler

import (
	"errors"
	"fmt"
	"log"
	"moft/admin"
	"moft/database"
	"moft/model"
	"net/http"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

type UserHandler struct {
	db *gorm.DB
}

// need auth admin and session middleware.
func NewUserHandler() *UserHandler {
	return &UserHandler{db: database.DB()}
}

func (h *UserHandler) Create(c *gin.Context) {
	var user struct {
		UserName string `json:"user_name" binding:"min=5,max=20"`
		Password string `json:"password" binding:"min=6,max=20"`
		Email    string `json:"email" binding:"email"`
		RoleName string `json:"role_name" binding:"required"`
	}
	if err := c.ShouldBindJSON(&user); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "the request body error",
			"message": err.Error(),
		})
		return
	}

	// check user role name.
	r, err := model.FindRoleByName(h.db, user.RoleName)
	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "the role name is error",
		})
		return
	}
	if model.IsAdminByRole(user.RoleName) && !admin.AssertByID(c.GetString("user_id")) {
		c.JSON(http.StatusForbidden, gin.H{
			"error": "no premission to create admin user",
		})
		return
	}

	// check duplicate users.
	var repeat bool = true
	_, err = model.FindUserByName(h.db, user.UserName)
	if errors.Is(err, gorm.ErrRecordNotFound) {
		repeat = false
	} else if err != nil {
		log.Printf("the list user by user name and email failed: %v", err)
		c.Status(http.StatusInternalServerError)
		return
	}
	if repeat {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "the user name is exist",
		})
		return
	}

	// create user.
	err = model.CreateUser(h.db, &model.User{
		UserName: user.UserName,
		Password: user.Password,
		Email:    user.Email,
		RoleID:   r.ID,
		RoleName: user.RoleName,
	})
	if err != nil {
		log.Printf("insert user into database failed: %v", err)
		c.Status(http.StatusInternalServerError)
		return
	}

	c.Status(http.StatusCreated)
}

func (h *UserHandler) List(c *gin.Context) {
	users, err := model.ListUser(h.db, nil)
	if err != nil {
		c.Status(http.StatusInternalServerError)
		return
	}

	uu := make([]model.User, 0, len(users))
	uid := c.GetString("user_id")
	isAdmin := c.GetBool("is_admin")

	switch {
	case admin.AssertByID(uid):
		uu = users
	case isAdmin:
		for _, u := range users {
			if admin.AssertByID(u.ID) {
				continue
			}
			uu = append(uu, u)
		}
	default:
		for _, u := range users {
			if model.IsAdminByRole(u.RoleName) {
				continue
			}
			uu = append(uu, u)
		}
	}

	c.JSON(http.StatusOK, gin.H{
		"users": uu,
	})
}

func (h *UserHandler) Delete(c *gin.Context) {
	userID := c.Param("user_id")

	err := model.DeleteUser(h.db, userID)
	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
		c.JSON(http.StatusNotFound, gin.H{
			"error": fmt.Sprintf("the user %s not found", userID),
		})
		return
	} else if err != nil {
		log.Printf("find user by id %s failed", userID)
		c.Status(http.StatusInternalServerError)
		return
	}

	c.Status(http.StatusNoContent)
}
